/*
 * Copyright 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package edu.upb.acs.jobsmaps.activity.login;

import android.content.Intent;
import android.os.AsyncTask;
import android.util.Log;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import edu.upb.acs.jobsmaps.activity.LoginActivity;
import edu.upb.acs.jobsmaps.activity.MainScreenActivity;
import edu.upb.acs.jobsmaps.database.mysql.ExternalDatabaseHelper;
import edu.upb.acs.jobsmaps.database.task.AsyncTaskResult;
import edu.upb.acs.jobsmaps.entity.User;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Display personalized greeting. This class contains boilerplate code to consume the token but
 * isn't integral to getting the tokens.
 */
public class GetGoogleUserDetailsTask extends AsyncTask<Void, Void, AsyncTaskResult> {
		private static final String TAG = "GetGoogleUserDetailsTask";
	private static final String NAME_KEY = "given_name";
	private static final String FIRST_NAME_KEY = "family_name";
	private static final String LAST_NAME_KEY = "given_name";

	protected LoginActivity mActivity;

	protected String mScope;
	protected String mEmail;

	public GetGoogleUserDetailsTask(LoginActivity activity, String email, String scope) {
		this.mActivity = activity;
		this.mScope = scope;
		this.mEmail = email;
	}

	@Override
	protected AsyncTaskResult doInBackground(Void... params) {
		try {
			mActivity.show("Login with email = " + mEmail);

			User user = fetchNameFromProfileServer();
			Thread.sleep(1);

			Log.d(TAG, "Done waiting");
			Intent intent = new Intent(mActivity, MainScreenActivity.class);
			intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
			intent.putExtra("user_email", user.getEmail());
			mActivity.startActivity(intent);

			return new AsyncTaskResult(user);
		} catch (IOException ex) {
			onError("Following Error occured, please try again. " + ex.getMessage(), ex);
			return new AsyncTaskResult(ex);
		} catch (JSONException e) {
			onError("Bad response: " + e.getMessage(), e);
			return new AsyncTaskResult(e);
		} catch (InterruptedException e) {
			onError("Bad response: " + e.getMessage(), e);
			return new AsyncTaskResult(e);
		}
	}

	protected void onError(String msg, Exception e) {
		if (e != null) {
//			Log.e(TAG, "Exception: ", e);
		}
		mActivity.show(msg);  // will be run in UI thread
	}

	/**
	 * Get a authentication token if one is not available. If the error is not recoverable then
	 * it displays the error message on parent activity.
	 */
	protected String fetchToken() throws IOException {
		try {
			return GoogleAuthUtil.getToken(mActivity, mEmail, mScope);
		} catch (UserRecoverableAuthException userRecoverableException) {
			// GooglePlayServices.apk is either old, disabled, or not present, which is
			// recoverable, so we need to show the user some UI through the activity.
			mActivity.handleException(userRecoverableException);
		} catch (GoogleAuthException fatalException) {
			onError("Unrecoverable error " + fatalException.getMessage(), fatalException);
		}
		return null;
	}

	/**
	 * Contacts the user info server to get the profile of the user and extracts the first name
	 * of the user from the profile. In order to authenticate with the user info server the method
	 * first fetches an access token from Google Play services.
	 *
	 * @throws java.io.IOException    if communication with user info server failed.
	 * @throws org.json.JSONException if the response from the server could not be parsed.
	 */
	private User fetchNameFromProfileServer() throws IOException, JSONException {
		String token = fetchToken();
//		Log.d(TAG, "************* token = " + token);
		if (token == null) {
			// error has already been handled in fetchToken()
			return null;
		}
		URL url = new URL("https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + token);
		HttpURLConnection con = (HttpURLConnection) url.openConnection();
		int sc = con.getResponseCode();
		if (sc == 200) {
			InputStream is = con.getInputStream();

			String jsonResponse = readResponse(is);
			JSONObject profile = new JSONObject(jsonResponse);

			User user = convertJsonToUser(profile, token, mEmail);
			is.close();
			return user;
//			updateUserDetailsInDatabase(jsonResponse);

//			String name = getFirstName(jsonResponse);
//			mActivity.show("Hello " + name + "!");
		} else if (sc == 401) {
			GoogleAuthUtil.invalidateToken(mActivity, token);
			onError("Server auth error, please try again.", null);
//			Log.i(TAG, "Server auth error: " + readResponse(con.getErrorStream()));
			return null;
		} else {
			onError("Server returned the following error code: " + sc, null);
			return null;
		}
	}


	/**
	 * Reads the response from the input stream and returns it as a string.
	 */
	private static String readResponse(InputStream is) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		byte[] data = new byte[2048];
		int len = 0;
		while ((len = is.read(data, 0, data.length)) >= 0) {
			bos.write(data, 0, len);
		}
		return new String(bos.toByteArray(), "UTF-8");
	}

	/**
	 * Parses the response and returns the first name of the user.
	 *
	 * @throws org.json.JSONException if the response is not JSON or if first name does not exist in response
	 */
	private String getFirstName(String jsonResponse) throws JSONException {
		JSONObject profile = new JSONObject(jsonResponse);
//		Log.d(TAG, "**************** profile = " + profile.toString());

		return profile.getString(NAME_KEY);
	}

	private User convertJsonToUser(JSONObject profile, String token, String mEmail) throws JSONException {
		User user = ExternalDatabaseHelper.getUserAfterEmail(mEmail);

		if (null == user) {
//				Log.d(TAG, "User is not in the database, add it");

			user = new User();
			user.setEmail(mEmail);
			user.setGoogleId(token);
			user.setFirstName(profile.getString(FIRST_NAME_KEY));
			user.setLastName(profile.getString(LAST_NAME_KEY));
			user.setUsername(mEmail.substring(0, mEmail.indexOf("@")));

			ExternalDatabaseHelper.addUser(user);

		} else {
//				Log.d(TAG, "User is already in the database");
		}
		return user;


	}

}
